`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file EthernetInterface.v
	@author Andrew D. Zonenberg
	@brief Ethernet packet interface
	
	Reads packets from device-specific drivers, reads headers, verifies frame checksums,
	and dispatches to IPv4 or IPv6 stack as appropriate.
 */
module EthernetInterface(
	clk,
	
	//Bus to NIC
	nic_ready,
	cmd_enable, cmd_opcode, cmd_data_in, cmd_busy, cmd_data_out, cmd_data_valid,
	link_state_interrupt, link_state, duplex_state, packet_ready,
	
	//Bus to NULL layer3 module
	null_packet_ready, null_read_word, null_rx_data, null_rx_rdy, null_read_done,
	
	uart_tx, uart_rx
    );
	 
	///////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations

	//Global
	input wire clk;
	
	input wire nic_ready;
	
	//Command bus to Ethernet controller
	output reg cmd_enable = 0;
	output reg[3:0] cmd_opcode = 0;
	output reg[15:0] cmd_data_in = 0;
	input wire cmd_busy;
	input wire[15:0] cmd_data_out;
	input wire cmd_data_valid;
	
	//Interrupts and status flags from the Ethernet controller
	input wire link_state_interrupt;
	input wire link_state;
	input wire duplex_state;
	input wire packet_ready;
	
	//Bus to null packet sink
	output reg null_packet_ready = 0;
	input wire null_read_word;
	output reg[15:0] null_rx_data = 0;
	output reg null_rx_rdy = 0;
	input wire null_read_done;
	
	//Debug port for LA
	output wire uart_tx;
	input wire uart_rx;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// The NIC driver itself is NOT contained within this module. We just talk to it.
	// Opcode definitions
	
	localparam OP_NOP				= 4'h0;	//do nothing
	localparam OP_GETMAC			= 4'h1;	//read MAC address (3 words)
	localparam OP_READSTART		= 4'h2;	//Prepare to read a packet (outputs the length on cmd_data_out)
	localparam OP_READSTREAM	= 4'h3;	//Read the next 16 bits of the current packet.
													//Attempting to read beyond the end of the packet returns all zeros.
	localparam OP_READDONE		= 4'h4;	//After completely reading the packet, use this instruction to
													//free the packet buffer
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// State definitions
	
	localparam STATE_READY			= 4'h0;
	localparam STATE_POST_1			= 4'h1;
	localparam STATE_POST_2			= 4'h2;
	localparam STATE_FREEZE			= 4'h3;
	localparam STATE_READ_START	= 4'h4;
	localparam STATE_READ_MACS		= 4'h5;
	localparam STATE_READ_TYPE		= 4'h6;
	localparam STATE_READ_PACKET	= 4'h7;
	localparam STATE_READ_HOLD		= 4'h8;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Long-term state
	
	reg[47:0] mac_address = 0;					//Our local MAC address
	
	reg got_first_packet = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////
	//Cracked headers for the current frame
	reg[15:0] frame_length = 0;				//Length of the current frame including headers
	reg[47:0] frame_src_mac = 0;				//Source MAC for this frame
	reg[47:0] frame_dst_mac = 0;				//Destination MAC for this frame
	reg[15:0] frame_ethertype = 0;			//Ethertype of this frame
	reg[15:0] frame_payload_length = 0;		//Length of the current frame excluding headers and FCS
														//frame_length - 'd18 bytes
														
	////////////////////////////////////////////////////////////////////////////////////////////////
	//Table of well-known ethertypes
	localparam ETHERTYPE_IPV4		= 16'h0800;
	localparam ETHERTYPE_ARP		= 16'h0806;
	localparam ETHERTYPE_IPV6		= 16'h86DD;
														
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Multiplexed command bus for the current layer-3 module
	reg read_word = 0;
	reg read_done = 0;
	
	always @(null_read_word, null_read_done, frame_ethertype) begin
		read_word <= 0;
		read_done <= 0;
		
		case(frame_ethertype)
			default: begin
				read_word <= null_read_word;
				read_done <= null_read_done;
			end
		endcase
		
	end
	
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Main state machine and temporary data
	
	reg[7:0] count = 0;							//General purpose temporary counter
	
	//Debug stuff
	reg packet_ready_buf = 0;

	reg[3:0] state = STATE_POST_1;
	always @(posedge clk) begin
		
		cmd_enable <= 0;
		cmd_opcode <= OP_NOP;
		cmd_data_in <= 0;
		
		//clear out buses to layer3 controllers
		null_packet_ready <= 0;
		null_rx_data <= 0;
		null_rx_rdy <= 0;
		
		packet_ready_buf <= packet_ready;
		if(packet_ready_buf && !packet_ready)
			got_first_packet <= 1;
		
		case(state)
		
			//////////////////////////////////////////////////////////////////////////////////////////
			// Wait for something interesting to happen, then dispatch it
			STATE_READY: begin
				
				//Packet just arrived? Start reading it
				if(packet_ready && !cmd_busy) begin
					cmd_enable <= 1;
					cmd_opcode <= OP_READSTART;
					state <= STATE_READ_START;
				end
				
			end
			
			//////////////////////////////////////////////////////////////////////////////////////////
			// Debug only - sink state we never leave
			STATE_FREEZE: begin
				
			end
			
			//////////////////////////////////////////////////////////////////////////////////////////
			// Initialization
			
			//Wait for the controller to turn on and get a link, then ask for our MAC address
			STATE_POST_1: begin
				if(nic_ready && link_state && !cmd_busy) begin
					cmd_enable <= 1;
					cmd_opcode <= OP_GETMAC;
					state <= STATE_POST_2;
					count <= 0;
				end
			end
			
			//Read our MAC address
			STATE_POST_2: begin
				if(cmd_data_valid) begin
					case(count)
						0: begin
							mac_address[47:32] <= cmd_data_out;
							count <= 1;
						end
						1: begin
							mac_address[31:16] <= cmd_data_out;
							count <= 2;
						end
						2: begin
							mac_address[15:0] <= cmd_data_out;
							state <= STATE_READY;
						end
					endcase
				end
			end
			
			//////////////////////////////////////////////////////////////////////////////////////////
			// Read datapath
			
			//Read packet length
			STATE_READ_START: begin
				if(cmd_data_valid) begin
					frame_length <= cmd_data_out;
					frame_payload_length <= cmd_data_out - 16'd18;
					
					//Read source and dest MAC addresses (3 words each)
					count <= 0;
					cmd_enable <= 1;
					cmd_opcode <= OP_READSTREAM;
					state <= STATE_READ_MACS;
				end
			end
			
			//Read source and dest MAC addresses
			STATE_READ_MACS: begin
				if(cmd_data_valid) begin
					
					//Read another word
					cmd_enable <= 1;
					cmd_opcode <= OP_READSTREAM;
					count <= count + 1;
				
					//Store data and decide what to do next
					case(count)
						0: frame_dst_mac[15:0] <= cmd_data_out;
						1: frame_dst_mac[31:16] <= cmd_data_out;
						2: frame_dst_mac[47:32] <= cmd_data_out;
						3: frame_src_mac[15:0] <= cmd_data_out;
						4: frame_src_mac[31:16] <= cmd_data_out;
						5: begin
							frame_src_mac[47:32] <= cmd_data_out;
							state <= STATE_READ_TYPE;
						end
					endcase

				end
			end
			
			//Read ethertype field
			STATE_READ_TYPE: begin
				if(cmd_data_valid) begin
					frame_ethertype <= cmd_data_out;
					
					//Notify the appropriate layer-3 module
					case(frame_ethertype)
						default: begin
							null_packet_ready <= 1;
							null_rx_rdy <= 1;
							null_rx_data <= frame_payload_length;
						end
					endcase
					
					//Wait for further commands from the l3 module
					state <= STATE_READ_PACKET;
					
				end
			end
			
			//Read packet data on request from the Layer 3 module
			//TODO: prefetching to reduce latency
			STATE_READ_PACKET: begin
			
				//Done reading
				if(read_done) begin
					cmd_enable <= 1;
					cmd_opcode <= OP_READDONE;
					state <= STATE_READ_HOLD;
				end
			
			end
			
			STATE_READ_HOLD: begin
				if(!cmd_busy) begin
					state <= STATE_READY;
				end
			end
			
		endcase
		
	end
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Logic analyzer
	RedTinUARTWrapper la(
		.clk(clk), 
		.din({
				nic_ready,						//1	127
				cmd_enable,						//1	126
				cmd_opcode,						//4	122
				cmd_data_in,					//16	106
				cmd_busy,						//1	105
				cmd_data_out,					//16	89
				cmd_data_valid,				//1	88
				link_state_interrupt,		//1	87
				link_state,						//1	86
				duplex_state,					//1	85
				packet_ready,					//1	84
				frame_ethertype,				//16	68
				state,							//4	64
				read_word,						//1	63
				read_done,						//1	62
				null_packet_ready,			//1	61
				got_first_packet,				//1	60
				60'h0
				}), 
		.uart_tx(uart_tx), 
		.uart_rx(uart_rx)
		);
	
endmodule
